package cn.bo.cloud.demo2.web;


import cn.bo.cloud.demo2.domain.dto.User;
import com.alibaba.csp.sentinel.EntryType;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;

/**
 * 注解方式配置sentinel降级规则
 */

@RestController
@RequestMapping("/server3")
public class ServerController3 {

    @Value("${server.port}")
    String port;

    private static final String RESOURCE_NAME = "s3uri1";

    /**
     * @SentinelResource 改善接口中资源定义和被流控降级后的处理方法
     * 怎么使用： 1. 添加依赖<artifactId>sentinel-annotation-aspectj</artifactId>
     *             2. 配置bean--SentinelResourceAspect
     *
     * value 定义资源
     * blockHandler 设置流量降级后处理方法（默认该方法必须和原方法声明在同一个类中）
     *      如果不想在同一个类，可以设置blockHandlerClass  ,不过需要注意，函数必须声明为static
     * fallback 当接口出现了异常，就可以交给fallback处理
     * ps: blockHandler和fallback同时指定的话，blockHandler优先级更高
     * exceptionsToIgnore 排除哪些异常不处理
     * @return
     */
    @RequestMapping("/s3uri1")
    @SentinelResource(value = RESOURCE_NAME, entryType = EntryType.IN,
            blockHandler = "blockHandlerForGetUser",
            fallback = "fallbackHandlerForGetUser"
    )
    public User test01(){

        throw new RuntimeException("异常");

//        return new User("server2-u1-" + port);

    }

    /**
     * 1. 一定要public
     * 2. 返回值一定要和原方法保证一致
     * 3. 可以在参数最后添加BlockException 可以区分是什么规则的异常处理方法
     * @param ex
     * @return
     */
    public User blockHandlerForGetUser(BlockException ex){
//        ex.printStackTrace();
//        fl.printStackTrace();
        return new User("被流控了");
    }

    public User fallbackHandlerForGetUser(Throwable throwable){
//        ex.printStackTrace();
//        fl.printStackTrace();
        return new User("异常处理");
    }


    @PostConstruct
    private static void initFlowRules(){
        // 降级规则，同一个资源可以有多个降级规则
        List<DegradeRule> rules = new ArrayList<>();

        // 降级
        DegradeRule rule = new DegradeRule();
        // 设置受保护的资源
        rule.setResource(RESOURCE_NAME);
        // 设置降级规则（熔断策略）：慢调用比例、异常比例、异常数
        rule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT);
        // 触发熔断异常数。慢调用比例模式下为慢调用临界值RT（超出该值计为慢调用）；异常比例/异常数模式下为对应的阈值
        rule.setCount(2);
        // 熔断时长，单位：s
        // 一旦触发了熔断，再次请求对应的接口就会直接调用降级方法
        // 10s过后--半开状态：恢复接口请求调用，如果第一次请求就异常，再次熔断，不会根据设置参数进行判定
        rule.setTimeWindow(10);
        // 熔断触发的最小请求数，请求数小于该值时即使异常比率超出阈值也不会熔断（1.7.0引入）
        rule.setMinRequestAmount(2);
        // 统计时常（单位ms），如60*1000代表分钟级（1.8.0引入）
//        rule.setStatIntervalMs(60*1000);

        // 1min内：执行了2次  出现了2次异常 就会触发熔断

        // 慢调用比例阈值，仅慢调用比例模式有效（1.8.0引入）
//        rule.setSlowRatioThreshold(0.5);
        rules.add(rule);

        DegradeRuleManager.loadRules(rules);

    }

}
